package org.yunai.yfserver.persistence.updater;

import org.slf4j.Logger;
import org.yunai.yfserver.common.LoggerFactory;
import org.yunai.yfserver.common.constants.CommonErrorLogInfo;
import org.yunai.yfserver.persistence.PersistenceObject;

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * 数据更新接口的默认实现.<br />
 * User: yunai
 * Date: 13-4-9
 * Time: 下午3:33
 */
public abstract class AbstractDataUpdater implements DataUpdater<PersistenceObject> {

    protected final static Logger LOGGER = LoggerFactory.getLogger(LoggerFactory.Logger.updater, AbstractDataUpdater.class);

    /**
     * 保存需要更新的元素<br />
     * <实体的ID, 将要被更新的元素>
     */
    private Map<Object, UpdateEntry<PersistenceObject>> changedObjects = new LinkedHashMap<Object, UpdateEntry<PersistenceObject>>();
    /**
     * 是否正在执行Update操作
     */
    private volatile boolean isUpdating = false;

    /**
     * 根据PersistenceObject生成KEY
     * @param po
     * @return KEY
     */
    private String genKey(PersistenceObject po) {
        return po.getClass().toString() + ":" + po.getId();
    }

    /**
     * 提交修改<br />
     * {@link #isUpdating}理论来说不会出现为true的情况。因为：设计的时候，考虑的，所有数据更新都是提交给<br />
     * {@link org.yunai.yfserver.async.IIoOperationService#asyncExecute(org.yunai.yfserver.async.IIoOperation)}来执行的，<br />
     * 而此时，会是所有逻辑处理完后，才进行{@link #process()}
     *
     * @param po 修改对象
     * @return 修改是否成功
     */
    @Override
    public boolean addSave(PersistenceObject po) {
        if (isUpdating) {
            LOGGER.error("[addSave] [The process operation is running,can't add new process].");
            return false;
        }
        changedObjects.put(genKey(po), new UpdateEntry<PersistenceObject>(UpdateEntry.Operation.UPDATE, po));
        return true;
    }

    /**
     * 提交删除<br />
     * @see AbstractDataUpdater#addSave(org.yunai.yfserver.persistence.PersistenceObject)
     *
     * @param po 修改对象
     * @return 修改是否成功
     */
    @Override
    public boolean addDelete(PersistenceObject po) {
        if (isUpdating) {
            LOGGER.error("[addDelete] [The delete operation is running,can't add new process].");
            return false;
        }
        changedObjects.put(genKey(po), new UpdateEntry<PersistenceObject>(UpdateEntry.Operation.DELETE, po));
        return true;
    }

    @Override
    public void process() {
        try {
            this.isUpdating = true;
            long beginTime = System.nanoTime();
            final int beginSize = this.changedObjects.size();
            if (beginSize > 0) {
                LOGGER.debug("[process] [Begin to sync objects size:{}].", this.changedObjects.size());
            }
            for (UpdateEntry<PersistenceObject> entry : changedObjects.values()) {
                PersistenceObject po = entry.getObj();
                try {
                    switch (entry.getOperation()) {
                        case UPDATE:
                            doUpdate(po);
                            break;
                        case DELETE:
                            doDelete(po);
                            break;
                    }
                } catch (Exception e) {
                    LOGGER.error("[process][{}] key[{}] error[{}].", CommonErrorLogInfo.DB_OPERATE_FAIL, genKey(po), e);
                }
            }
            final int finishSize = changedObjects.size();
            if (finishSize > 0) {
                LOGGER.debug("[process] [Finish sync objects size:{}].", finishSize);
            }
            if (beginSize != finishSize) {
                LOGGER.error("[process][{}] [The begin size:{}] [but the" +
                        "finish size:{}].", CommonErrorLogInfo.INVALID_STATE, beginSize, finishSize);
            }
            long costTime = (System.nanoTime() - beginTime) / 1000000;
            if (costTime > 0) {
                LOGGER.debug("[process] [Update Time:{} ms].", costTime);
            }
        } finally {
            this.changedObjects.clear();
            this.isUpdating = false;
        }
    }

    /**
     * 返回需要更新的对象
     *
     * @return 需要更新的对象
     */
    public Map<Object, UpdateEntry<PersistenceObject>> getChangedObjects() {
        return Collections.unmodifiableMap(changedObjects);
    }

    /**
     * 执行更新对象
     * @param po 更新的对象
     */
    protected abstract void doUpdate(PersistenceObject po);

    /**
     * 更新删除对象
     * @param po 删除的对象
     */
    protected abstract void doDelete(PersistenceObject po);

    public boolean isUpdating() {
        return isUpdating;
    }
}
